<?php

include_once(dirname(__FILE__).'/service.php');
include_once(dirname(__FILE__).'/classes_database/services.database.filesystem.php');

//define('FILESYSTEM_CONFIG_FILE', dirname(__FILE__).'/conf/sys_config.xml');

// Si esta constante se define como true, se emite una excepción si no se puede borrar la carpeta de algún repositorio (porq tiene archivos EXTRA agregados no via el FS)
if(!defined('__STRICT_REP_REMOVE'))
    define('__STRICT_REP_REMOVE',false);

// Error MeSsaGges:
define('__CONFIG_FILE_EMSG'        , 'Config file not found!'        ); // Config file not found!
define('__REPO_NOT_FOUND_EMSG'     , 'Repository not Found!'         ); // Repository not Found!
define('__CANT_CREATE_REPO_EMSG'   , 'Unable to create repository.'  ); // Unable to create repository.
define('__CANT_CREATE_FILE_EMSG'   , 'Unable to create file.'        ); // Unable to create file.
define('__FILE_ALREADY_EXISTS_EMSG', 'File already exists.'          ); // File already exists.
define('__FILE_NOT_FOUND_EMSG'     , 'File not found!'               ); // File not found.
define('__CANT_DEL_FILE_EMSG'      , 'Unable to remove File.'        ); // Unable to remove File.
define('__CANT_DEL_REPO_EMSG'      , 'Unable to remove Repository.'  ); // Unable to remove Repository.
define('__CANT_CREATE_ZIP_EMSG'    , 'Unable to create Zip file.'    ); // Unable to create Zip file.
define('__EMPTY_REP_EMSG'          , 'The reposiroty is empty.'      ); // The reposiroty is empty.
define('__UNKNOWN_EMSG'            , 'An unknown error has ocurred.' ); // An unknown error has ocurred.



//Esta función es para solucionar todos los problemas asociados a finfo y las versiones de php (deberá ir en el common)
function get_mimetype($file)
{
    if($file==null)
        return null;
    
    if( @defined(FILEINFO_NONE) )
    {
        if( @defined(FILEINFO_MIME_TYPE) )
        {
            $finfo = new finfo(FILEINFO_MIME_TYPE);
            return $finfo->file($file);
        }
        else
        {
            $finfo = new finfo(FILEINFO_MIME);
            list($mime_type, $encoding) = explode(';', $finfo->file($file), 2 );
            return $mime_type;
        }
    }
    else
        return 'image/finfo_not_installed';
}

function is_valid_image($file)
{
    return preg_match('/^image/',get_mimetype($file))!=0;
}

class FileSystem extends Service
{

    private $filesystemBasePath = null;
    private $disableWhitelist = false;

    public function FileSystem($config_file)
    {
        $this->db  =  DBFileSystem::getInstance();
        
        $config = simplexml_load_file($config_file);
        
        if( !$config || !isset($config->file_system) || !isset($config->file_system->path) )
            throw new Exception(__CONFIG_FILE_EMSG);
        $this->filesystemBasePath = realpath($config->file_system->path).'/';
        if(isset($config->file_system->debug))
            $this->disableWhitelist = ($config->file_system->debug == 'true');
    }

    /*
        Method : getRepositoryPath
        Desc   : Devuleve el path de un repositorio dado.
    */
    private function getRepositoryPath($repository)
    {
        $result = $this->db->getRepository($repository);
        if(!$result)
            throw new Exception(__REPO_NOT_FOUND_EMSG);
        return $result['path_fsr'];
    }

    /*
        Method : createRepository
        Desc   : Crea un repositorio con el nombre dado, si parentRepository no es null, este se creará como un subdirectorio del padre.
    */
    public function createRepository($reposirotyName,$parentRepository=null)
    {
        $mode        = 0700;
        $recursive    = true;

        if($parentRepository){
            $parentPath = $this->getRepositoryPath($parentRepository);
            $dirPath = $parentPath.$reposirotyName;
        }
        else
            $dirPath = $this->filesystemBasePath.$reposirotyName;

        // crea el directorio
        if(!file_exists($dirPath))
            if( !@mkdir($dirPath,$mode,$recursive) )
                throw new Exception(__CANT_CREATE_REPO_EMSG);
        elseif(!is_dir($dirPath))
            throw new Exception(__CANT_CREATE_REPO_EMSG);
        

        // lo inserta en la DB
        $id_repository = $this->db->insertRepository($reposirotyName,$dirPath.'/',$parentRepository);

        return $id_repository;
    }

    /*
        Method : putFile
        Desc   : Guarda el archivo dado en el repositorio dado, opcionalmente se puede renombrar el archivo.
    */
    public function putFile($fromFile,$repository,$filename=null,$overWrite=true)
    {
        $filename = $filename?$filename:basename($fromFile);
        $mime_type = get_mimetype($fromFile);
        $file_length = filesize($fromFile);

        $repoPath = $this->getRepositoryPath($repository);

        $repoFilePath = $repoPath.$filename;

		if(!$overWrite && file_exists($repoFilePath) )
			throw new Exception(__FILE_ALREADY_EXISTS_EMSG);
		
        if ( ! copy($fromFile,$repoFilePath) )
            throw new Exception(__CANT_CREATE_FILE_EMSG);

        $id_fsf = $this->db->insertFile($filename,$mime_type,$file_length,$repository);

        return $id_fsf;
    }

    /*
        Method : getFile
        Desc   : Devuelve la información y el path del archivo dado. Si $getContent = true, también devuelve el content del file.
    */
    public function getFile($id_fsf, $getContent = false )
    {
        $file = $this->db->getFile($id_fsf);

        if(!$file)
            throw new Exception (__FILE_NOT_FOUND_EMSG);
        
        $result = array();
        
        $result['filename']  = $file['filename_fsf'];
        $result['mime_type'] = $file['mimetype_fsf'];
        $result['length']    = $file['length_fsf'];
        $result['file_path'] = $file['path_ffi'];

        if($getContent)
            $result['content'] = file_get_contents($result['file_path']);
        
        return $result;
    }    
    
    /*
        Method : removeFile
        Desc   : Elimina el archivo.
    */
    public function removeFile($id_fsf)
    {    
        $file = $this->db->getFile($id_fsf);
        
        if(!$file)
            throw new Exception (__FILE_NOT_FOUND_EMSG);
            
        if( !@unlink($file['path_ffi']) )
            throw new Exception (__CANT_DEL_FILE_EMSG);
        
        $this->db->removeFile($id_fsf);
        
        return true;
    }

    /*
        Method : removeRepository
        Desc   : Elimina el repositorio dado y todos los archivos que este contine. (NOTA: por el momento no se permite eliminar repositorios que tengan otros repositorios hijos asociados, se deberán ir borrando los repositorios de atras para adelante.
    */
    public function removeRepository($repository)
    {
        $this->db->StartTrans();
        
        $files = $this->db->getRepositoryContent($repository);
        
        foreach ($files['rows'] as $file){
            if( !@unlink($file['path_ffi']) )
                throw new Exception (__CANT_DEL_FILE_EMSG);
            $this->db->removeFile($file['id_fsf']);
        }
        
        $repo_path = realpath($this->getRepositoryPath($repository));
        if( !@rmdir($repo_path) && __STRICT_REP_REMOVE )
                throw new Exception (__CANT_DEL_REPO_EMSG);
        
        $this->db->removeRepository($repository);
        
        $success = !$this->db->HasFailedTrans();
        
        $this->db->CompleteTrans();
        
        return $success;
    }
    
    /*
        Method : zipRepository
        Desc   : debería crear un zipfile en $zipFile (path+nombre) y añadir TODOS los archivos de dicho repositorio.
    */
    public function zipRepository($repository,$zipFile)
    {
        $files =$this->db->getRepositoryContent($repository);
        
        if($files['results']<1)
            throw new Exception(__EMPTY_REP_EMSG);
        
        $zip = new ZipArchive();
        if ($zip->open($zipFile, ZIPARCHIVE::CREATE)!==TRUE)
            throw new Exception(__CANT_CREATE_ZIP_EMSG);
        
        foreach($files['rows'] as $file)
        {
            if(file_exists($file['path_ffi']))
                if(!$zip->addFile($file['path_ffi'],'/'.$file['filename_fsf']))
                    throw new Exception(__UNKNOWN_EMSG);
        }
        
        $zip->close();
        return true;
    }
    
    /*
        Method : isWhitelisted
        Desc   : Devuelve TRUE si el archivo está en la whiteList (es de accesso público). Si el FS está configurado en modo PÚBLICO siempre se devuelve TRUE.
    */
    public function isWhitelisted($id_fsf)
    {
        return ($this->disableWhitelist || $this->db->isWhitelisted($id_fsf));
    }
}